home *** CD-ROM | disk | FTP | other *** search
/ Nebula 2 / Nebula Two.iso / SourceCode / Palettes / TablePrinter / DBTableViewPrinter.m < prev    next >
Text File  |  1995-06-12  |  25KB  |  839 lines

  1.  
  2. // DBTableViewPrinter -- An object for printing DBTableViews.
  3. //
  4. // By Eric T. Seymour, NeXT Computer, Inc.
  5. //
  6. // This object formats and prints DBTableViews.  Basically, the drawself:
  7. // method draws the table (in brute force fashion).  It also contains methods
  8. // for setting options.
  9. //
  10. // You may freely copy, distribute, and reuse the code in this example.
  11. // NeXT disclaims any warranty of any kind, expressed or  implied, as to its
  12. // fitness for any particular use.
  13. //
  14. // This file looks best when using tabstops of 3.
  15.  
  16.  
  17. #import <dbkit/dbkit.h>
  18. #import <appkit/appkit.h>
  19. #import "DBTableViewPrinter.h"
  20. #import "TablePrintPanel.h"
  21.  
  22. #define    CURRENT_CLASS_VERSION    1
  23.  
  24. @interface DBTableViewPrinter(Private)
  25. - refreshLayout;                                // Page Layout stuff
  26. @end
  27.  
  28. @implementation DBTableViewPrinter
  29.  
  30. + initialize 
  31. {
  32.     // Set version for read/write backward compatibility
  33.     if ( self == [DBTableViewPrinter class] )
  34.         [DBTableViewPrinter setVersion:CURRENT_CLASS_VERSION];
  35.  
  36.     return self;
  37. }
  38.  
  39. - initFrame:(const NXRect *)frameRect
  40. {
  41.     // initialize the frame
  42.     [super initFrame:frameRect];
  43.     [self setFlipped:YES];
  44.  
  45.     // Initialize some flags and variables
  46.     tableView = nil;
  47.     rowHeight = 0.0;
  48.     pageNumSepChar = '-';
  49.     isPrinting = NO;
  50.     isPageNumbersEnabled = NO;
  51.     isRowNumbersEnabled = NO;
  52.     isSelectedRowsOnly = NO;
  53.     isForceDrawColor = NO;
  54.     startingPageNumber = 1;
  55.     startingRowNumber = 1;
  56.     isGridLinesOn = YES;
  57.     strcpy(rowNumberHeaderTitle,"#");
  58.     gridColor = NX_COLORBLACK;
  59.     backgroundColor = NX_COLORWHITE;
  60.     rowNumbersBackColor = NXConvertGrayToColor(0.17);
  61.  
  62.     // Create Cell with which to draw titles
  63.     headerTextCell = [[TextFieldCell alloc] initTextCell:""];
  64.     [headerTextCell setFont:
  65.         [Font boldSystemFontOfSize:12.0 matrix:NX_FLIPPEDMATRIX]];
  66.     [headerTextCell setWrap:NO];
  67.     [headerTextCell setAlignment:NX_CENTERED];
  68.     [headerTextCell setTextColor:NX_COLORWHITE];
  69.     [headerTextCell setTextGray:NX_WHITE];
  70.     [headerTextCell setBackgroundGray:0.17];
  71.     [headerTextCell calcCellSize:&headerTextCellSize];
  72.  
  73.     // Create Cell with which to draw row number headers
  74.     rowNumberHeaderCell = [[TextFieldCell alloc] initTextCell:""];
  75.     [rowNumberHeaderCell setFont:
  76.         [Font boldSystemFontOfSize:12.0 matrix:NX_FLIPPEDMATRIX]];
  77.     [rowNumberHeaderCell setWrap:NO];
  78.     [rowNumberHeaderCell setAlignment:NX_CENTERED];
  79.     [rowNumberHeaderCell setTextColor:NX_COLORWHITE];
  80.     [rowNumberHeaderCell setTextGray:NX_WHITE];
  81.     [rowNumberHeaderCell setBackgroundGray:0.17];
  82.  
  83.     // Create Cell with which to draw row numbers
  84.     rowNumberCell = [[TextFieldCell alloc] initTextCell:""];
  85.     [rowNumberCell setFont:
  86.         [Font boldSystemFontOfSize:12.0 matrix:NX_FLIPPEDMATRIX]];
  87.     [rowNumberCell setWrap:NO];
  88.     [rowNumberCell setAlignment:NX_RIGHTALIGNED];
  89.     [rowNumberCell setTextColor:NX_COLORWHITE];
  90.     [rowNumberCell setTextGray:NX_WHITE];
  91.     [rowNumberCell setBackgroundGray:-1.0];    // Make background 100% Alpha
  92.  
  93.     // Create Cell with which to draw page number
  94.     pageNumberCell = [[TextFieldCell alloc] initTextCell:""];
  95.     [pageNumberCell setWrap:NO];
  96.     [pageNumberCell setTextColor:NX_COLORBLACK];
  97.     [pageNumberCell setTextGray:NX_BLACK];
  98.     [pageNumberCell calcCellSize:&pageNumberCellSize];
  99.     [pageNumberCell setBackgroundGray:-1.0];
  100.  
  101.     // set my page frame
  102.     if ( frameRect )
  103.     {
  104.         pageFrame.origin.x = frameRect->origin.x;
  105.         pageFrame.origin.y = frameRect->origin.y;
  106.         pageFrame.size.width = frameRect->size.width;
  107.         pageFrame.size.height = frameRect->size.height;
  108.     }
  109.     else 
  110.     {
  111.         pageFrame.origin.x = pageFrame.origin.y = 0.0;
  112.         pageFrame.size.width = pageFrame.size.height = 20.0;
  113.     }
  114.  
  115.     return self;
  116. }
  117.  
  118. // Toggles and methods for color drawing
  119. - setForceDrawColor:(BOOL)toggle
  120. {
  121.     isForceDrawColor = toggle;
  122.     return self;
  123. }
  124. - (BOOL)isForceDrawColor{return isForceDrawColor;}
  125.  
  126. - (BOOL)shouldDrawColor
  127. {
  128.     if ( isForceDrawColor ) return YES;
  129.     else return [super shouldDrawColor];
  130. }
  131.  
  132. - free
  133. {
  134.     // Free the Cells I was using
  135.     [headerTextCell free];
  136.     [rowNumberHeaderCell free];
  137.     [rowNumberCell free];
  138.     [pageNumberCell free];
  139.     return [super free];
  140. }
  141.  
  142. - setPageNumbersEnabled:(BOOL)toggle
  143. {
  144.     isPageNumbersEnabled = toggle;
  145.     if ( isPrinting ) [self refreshLayout];
  146.     return self;
  147. }
  148. - (BOOL)isPageNumbersEnabled{return isPageNumbersEnabled;}
  149.  
  150. - setStartingPageNumber:(int)num
  151. {
  152.     startingPageNumber = num;
  153.     return self;
  154. }
  155. - (int)startingPageNumber{return startingPageNumber;}
  156.  
  157. - setPageNumberSeparator:(char)ch
  158. {
  159.     if ( NXIsPrint(ch) ) pageNumSepChar = ch;
  160.     return self;
  161. }
  162. - (char)pageNumberSeparator{return pageNumSepChar;}
  163.  
  164. - setRowNumbersEnabled:(BOOL)toggle
  165. {
  166.     isRowNumbersEnabled = toggle;
  167.     if ( isPrinting ) [self refreshLayout];
  168.     return self;
  169. }
  170. - (BOOL)isRowNumbersEnabled{return isRowNumbersEnabled;}
  171.  
  172. - setStartingRowNumber:(int)num
  173. {
  174.     startingRowNumber = num;
  175.     if ( isPrinting ) [self refreshLayout];
  176.     return self;
  177. }
  178. - (int)startingRowNumber{return startingRowNumber;}
  179.  
  180. - setRowNumbersTitle:(const char *)title
  181. {
  182.     if ( title == NULL ) return nil;
  183.     memset(rowNumberHeaderTitle,0,51);
  184.     strncpy(rowNumberHeaderTitle,title,50);
  185.     if ( isPrinting ) [self refreshLayout];
  186.     return self;
  187. }
  188. - (const char *)rowNumbersTitle{return rowNumberHeaderTitle;}
  189.  
  190. - setGridLinesOn:(BOOL)toggle
  191. {
  192.     isGridLinesOn = toggle;
  193.     return self;
  194. }
  195. - (BOOL)isGridLinesOn{return isGridLinesOn;}
  196.  
  197. - setSelectedRowsOnly:(BOOL)toggle
  198. {
  199.     isSelectedRowsOnly = toggle;
  200.     if ( isPrinting ) [self refreshLayout];
  201.     return self;
  202. }
  203. - (BOOL)isSelectedRowsOnly{return isSelectedRowsOnly;}
  204.  
  205. // Set Headers Bezeled Toggle
  206.  
  207. - setColumnHeadersBezeled:(BOOL)toggle
  208. {
  209.     [headerTextCell setBezeled:toggle];
  210.     [rowNumberHeaderCell setBezeled:toggle];
  211.     [headerTextCell calcCellSize:&headerTextCellSize];
  212.     return self;
  213. }
  214. - (BOOL)isColumnHeadersBezeled{return [headerTextCell isBezeled];}
  215.  
  216. // Colors
  217.  
  218. - setColumnHeadersTextColor:(NXColor)color
  219. {
  220.     float    gray;
  221.  
  222.     NXConvertColorToGray(color,&gray);
  223.     [headerTextCell setTextColor:color];
  224.     [headerTextCell setTextGray:gray];
  225.     [rowNumberHeaderCell setTextColor:color];
  226.     [rowNumberHeaderCell setTextGray:gray];
  227.     return self;
  228. }
  229. - (NXColor)columnHeadersTextColor{return [headerTextCell textColor];}
  230.  
  231. - setColumnHeadersBackColor:(NXColor)color
  232. {
  233.     float    gray;
  234.  
  235.     NXConvertColorToGray(color,&gray);
  236.     [headerTextCell setBackgroundColor:color];
  237.     [headerTextCell setBackgroundGray:gray];
  238.     [rowNumberHeaderCell setBackgroundColor:color];
  239.     [rowNumberHeaderCell setBackgroundGray:gray];
  240.     return self;
  241. }
  242. - (NXColor)columnHeadersBackColor{return [headerTextCell backgroundColor];}
  243.  
  244. - setRowNumbersTextColor:(NXColor)color
  245. {
  246.     float    gray;
  247.  
  248.     NXConvertColorToGray(color,&gray);
  249.     [rowNumberCell setTextColor:color];
  250.     [rowNumberCell setTextGray:gray];
  251.     return self;
  252. }
  253. - (NXColor)rowNumbersTextColor{return [rowNumberCell textColor];}
  254.  
  255. - setRowNumbersBackColor:(NXColor)color
  256. {
  257.     rowNumbersBackColor = color;
  258.     return self;
  259. }
  260. - (NXColor)rowNumbersBackColor{return rowNumbersBackColor;}
  261.  
  262. - setGridLinesColor:(NXColor)color
  263. {
  264.     gridColor = color;
  265.     return self;
  266. }
  267. - (NXColor)gridLinesColor{return gridColor;}
  268.  
  269. - setPageNumberColor:(NXColor)color
  270. {
  271.     float    gray;
  272.  
  273.     NXConvertColorToGray(color,&gray);
  274.     [pageNumberCell setTextColor:color];
  275.     [pageNumberCell setTextGray:gray];
  276.     return self;
  277. }
  278. - (NXColor)pageNumberColor{return [pageNumberCell textColor];}
  279.  
  280. - setBackgroundColor:(NXColor)color
  281. {
  282.     backgroundColor = color;
  283.     return self;
  284. }
  285. - (NXColor)backgroundColor{return backgroundColor;}
  286.  
  287. - printPSCode:sender
  288. {
  289.     id    rv;
  290.  
  291.     // Print only if table has been set to a valid DBTableView or subclass
  292.     // thereof
  293.     if ( !tableView || ![tableView isKindOfClassNamed:"DBTableView"] )
  294.         return nil;
  295.  
  296.     // set all instance variables before printing
  297.     [self refreshLayout];
  298.     
  299.     // turn on printing flag and print
  300.     isPrinting = YES;
  301.     rv = [super printPSCode:sender];
  302.  
  303.     // turn off printing flag and return
  304.     isPrinting = NO;
  305.     return rv;
  306. }
  307.  
  308. // Setting/Get the current DBTableView
  309. - setTableView:table
  310. {
  311.     tableView = table;
  312.     return self;
  313. }
  314. - tableView{return tableView;}
  315.     
  316. - drawSelf:(const NXRect *)rects :(int)rectCount
  317. {
  318.     id                    aVector;
  319.     int                page,subPage,
  320.                         i,j,
  321.                         colStart,colEnd,colsThisPage;
  322.     unsigned int    row,actualRow,actualPageRowStart,pageRowStart,
  323.                         rowNumber,actualRowNumberStart;
  324.     double            tmpDouble;
  325.     NXSize            cellSize;
  326.     NXCoord            theSize;
  327.     char                pageNumberString[50];
  328.     float                gray;
  329.     NXColor            localBackgroundColor = backgroundColor,
  330.                         localGridColor = gridColor,
  331.                         localRowNumberBackColor = rowNumbersBackColor;
  332.     NXRect            aRect;
  333.     int                bezelDrawOffset = 0,
  334.                         bezelForwardOffset = 0;
  335.     BOOL                isBezeledHeaders = NO;
  336.  
  337.     // don't waste time if this is not a call from printPSCode
  338.     if ( !isPrinting ) return [super drawSelf:rects :rectCount];
  339.  
  340.     // set an offset for bezeled headers
  341.     if ( [headerTextCell isBezeled] )
  342.     {
  343.         bezelDrawOffset = 5;        // offset to forward origin.y after header
  344.         bezelForwardOffset = 4;    // offset to consider when advancing to next page
  345.         isBezeledHeaders = YES;
  346.     }
  347.     else
  348.     {
  349.         bezelDrawOffset = -1;        // offset to forward origin.y after header
  350.         bezelForwardOffset = -2;// offset to consider when advancing to next page
  351.         isBezeledHeaders = NO;
  352.     }
  353.  
  354.     // Clear to proper colors.  If drawing in B&W, then convert the gray values
  355.     if ( ![self shouldDrawColor] )
  356.     {
  357.         NXConvertColorToGray(backgroundColor,&gray);
  358.         localBackgroundColor = NXConvertGrayToColor(gray);
  359.         NXConvertColorToGray(gridColor,&gray);
  360.         localGridColor = NXConvertGrayToColor(gray);
  361.         NXConvertColorToGray(rowNumbersBackColor,&gray);
  362.         localRowNumberBackColor = NXConvertGrayToColor(gray);
  363.     }
  364.     NXSetColor(localBackgroundColor);
  365.     NXRectFill(&pageFrame);
  366.  
  367.     // Now go through and print each full tableview page
  368.     row = 0;
  369.     page = startingPageNumber;
  370.     theRect.origin.y = 0.0; 
  371.     actualRow = 0;
  372.     rowNumber = startingRowNumber;
  373.     while ( actualRow < maxRows )
  374.     {
  375.         colStart = 0;
  376.  
  377.         // determine how many cols can fit on this page
  378.         for ( colsThisPage = 0,tmpDouble = 0.0,i = colStart;i < maxColumns;i++ )
  379.         {
  380.             [headerTextCell setStringValue:[[columnList objectAt:i] title]];
  381.             [headerTextCell calcCellSize:&cellSize];
  382.             theSize = [(DBTableVector *)[columnList objectAt:i] size];
  383.             if ( theSize < cellSize.width ) theSize = cellSize.width;
  384.             tmpDouble += theSize;
  385.             if ( tmpDouble > rowNumberedWidth ) break;
  386.             else colsThisPage++;
  387.         }
  388.         if ( colsThisPage == 0 ) colsThisPage = 1;
  389.  
  390.         colEnd = colsThisPage;
  391.         pageRowStart = row;
  392.         actualPageRowStart = actualRow;
  393.         actualRowNumberStart = rowNumber;
  394.         subPage = 1;
  395.         while ( colStart < maxColumns )
  396.         {
  397.             row = pageRowStart;
  398.             actualRow = actualPageRowStart;
  399.             rowNumber = actualRowNumberStart;
  400.  
  401.             // Draw Page number if required
  402.             if ( isPageNumbersEnabled )
  403.             {
  404.                 theRect.origin.y += pageNumberHeight;
  405.                 if ( NXPointInRect(&theRect.origin,&rects[0]) )
  406.                 {                
  407.                     if ( pagesPerRow > 1 )
  408.                         sprintf(pageNumberString,"%d%c%d",
  409.                             page,pageNumSepChar,subPage);
  410.                     else sprintf(pageNumberString,"%d",page);
  411.                     [pageNumberCell setStringValue:pageNumberString];
  412.                     [pageNumberCell calcCellSize:&cellSize];
  413.                     theRect.origin.x = pageFrame.size.width - cellSize.width;
  414.                     theRect.size.width = cellSize.width;
  415.                     theRect.size.height = pageNumberHeight;
  416.                     [pageNumberCell drawSelf:&theRect inView:self];
  417.                 }            
  418.             }
  419.  
  420.             // Initialize the first draw rect for the header row
  421.             theRect.origin.x = 0.0;
  422.             theRect.origin.y += headerTextCellSize.height;
  423.             theRect.size.height = headerTextCellSize.height;
  424.  
  425.             // Put a header at the top of the page
  426.             if ( NXPointInRect(&theRect.origin,&rects[0]) )
  427.             {
  428.                 // Put the Row Number Header if required
  429.                 if ( isRowNumbersEnabled )
  430.                 {
  431.                     theRect.size.width = rowNumberHeaderCellSize.width;
  432.                     [rowNumberHeaderCell drawSelf:&theRect inView:self];
  433.  
  434.                     // Draw border around row number header
  435.                     if ( isGridLinesOn && !isBezeledHeaders )
  436.                     {
  437.                         // Set the color of the lines
  438.                         NXSetColor(localGridColor);
  439.  
  440.                         // Draw Left, Top, and Right Borders
  441.                         PSmoveto(theRect.origin.x,
  442.                             theRect.origin.y + rowNumberHeaderCellSize.height);
  443.                         PSlineto(theRect.origin.x,theRect.origin.y);
  444.                         PSlineto(theRect.origin.x +
  445.                             rowNumberHeaderCellSize.width - 1,
  446.                             theRect.origin.y);
  447.                         PSlineto(theRect.origin.x +
  448.                             rowNumberHeaderCellSize.width - 1,
  449.                             theRect.origin.y + rowNumberHeaderCellSize.height);
  450.                     }
  451.  
  452.                     theRect.origin.x += rowNumberHeaderCellSize.width;
  453.                 }
  454.                 
  455.                 // Put the rest of the header
  456.                 for ( i = colStart; i < colEnd; i++ )
  457.                 {
  458.                     aVector = [columnList objectAt:i];
  459.                     [headerTextCell setStringValue:[aVector title]];
  460.                     [headerTextCell calcCellSize:&cellSize];
  461.                     theSize = [(DBTableVector *)aVector size];
  462.                     if ( theSize < cellSize.width ) theSize = cellSize.width;
  463.                     theRect.size.width = theSize;
  464.                     [headerTextCell setAlignment:[aVector titleAlignment]];
  465.                     [headerTextCell drawSelf:&theRect inView:self];
  466.  
  467.                     // Draw border around each header
  468.                     if ( isGridLinesOn && !isBezeledHeaders )
  469.                     {
  470.                         // Set the color of the lines
  471.                         NXSetColor(localGridColor);
  472.  
  473.                         // Draw Top and Right Borders
  474.                         PSmoveto(theRect.origin.x,theRect.origin.y);
  475.                         PSlineto(theRect.origin.x + theRect.size.width - 1,
  476.                             theRect.origin.y);
  477.                         PSlineto(theRect.origin.x + theRect.size.width - 1,
  478.                             theRect.origin.y + theRect.size.height);
  479.                     }
  480.  
  481.                     theRect.origin.x += theSize;
  482.                 }
  483.             }
  484.  
  485.             // Now print rows on this page until no more fit
  486.             theRect.size.height = rowHeight;
  487.             theRect.origin.y += bezelDrawOffset;
  488.             for ( i = 0; i < maxRowsPerPage && actualRow < maxRows; row++ )
  489.             {
  490.                 // Skip if selection is on and this is not selected
  491.                 if ( isSelectedRowsOnly && ![tableView isRowSelected:row] )
  492.                     continue;
  493.  
  494.                 // Initialize the first draw rect for the current row
  495.                 theRect.origin.x = 0.0;
  496.                 theRect.origin.y += rowHeight;
  497.  
  498.                 // Now do each cell by having each formatter draw itself
  499.                 if ( NXPointInRect(&theRect.origin,&rects[0]) )
  500.                 {
  501.                     // Draw a Row Number Cell if required
  502.                     if ( isRowNumbersEnabled )
  503.                     {
  504.                         // Draw the background
  505.                         NXSetColor(localRowNumberBackColor);
  506.                         NXSetRect(&aRect,theRect.origin.x,theRect.origin.y + 1,
  507.                             rowNumberHeaderCellSize.width - 1,rowHeight);
  508.                         
  509.                         // If the first row, then narrow the gap above the first
  510.                         // row number with row number background color
  511.                         if ( actualRow == actualPageRowStart )
  512.                         {
  513.                             aRect.origin.y--;
  514.                             aRect.size.height++;
  515.                             if ( !isGridLinesOn )
  516.                             {
  517.                                 aRect.origin.y--;
  518.                                 aRect.size.height++;
  519.                             }
  520.                         }
  521.                         NXRectFill(&aRect);
  522.  
  523.                         // Draw the number using the cell
  524.                         [rowNumberCell setIntValue:rowNumber];
  525.                         NXSetRect(&aRect,theRect.origin.x,theRect.origin.y,
  526.                             rowNumberHeaderCellSize.width,rowHeight);
  527.                         [rowNumberCell drawSelf:&aRect inView:self];
  528.                         
  529.                         // Draw draw lines around row numbers if required
  530.                         if ( isGridLinesOn )
  531.                         {
  532.                             // Set the color of the lines
  533.                             NXSetColor(localGridColor);
  534.                         
  535.                             // Draw Left Border
  536.                             PSmoveto(theRect.origin.x,theRect.origin.y);
  537.                             PSlineto(theRect.origin.x,theRect.origin.y+rowHeight);
  538.                             
  539.                             // Always draw bottom and right borders
  540.                             PSlineto(theRect.origin.x +
  541.                                 rowNumberHeaderCellSize.width - 1,
  542.                                 theRect.origin.y + rowHeight);
  543.                             PSlineto(theRect.origin.x +
  544.                                 rowNumberHeaderCellSize.width - 1,
  545.                                 theRect.origin.y);
  546.                             
  547.                             // Only put top border on first rows
  548.                             if ( actualRow == actualPageRowStart )
  549.                                 PSlineto(theRect.origin.x - 1,theRect.origin.y);
  550.                         }
  551.                         theRect.origin.x += rowNumberHeaderCellSize.width;
  552.                     }
  553.  
  554.                     // Draw the rest of the cells
  555.                     for ( j = colStart; j < colEnd; j++ )
  556.                     {
  557.                         [headerTextCell
  558.                             setStringValue:[[columnList objectAt:j] title]];
  559.                         [headerTextCell calcCellSize:&cellSize];
  560.                         theSize = [(DBTableVector *)[columnList objectAt:j] size];
  561.                         if ( theSize < cellSize.width ) theSize = cellSize.width;
  562.  
  563.                         theRect.size.width = theSize;
  564.                         [[tableView formatterAt:row :j] beginBatching:nil];
  565.                         theRect.origin.y++;
  566.                         [[tableView formatterAt:row :j] drawFieldAt:row :j
  567.                             inside:&theRect inView:self
  568.                             withAttributes:[tableView rowAt:row]
  569.                             :[tableView columnAt:j] usePositions:YES :NO];
  570.                         theRect.origin.y--;
  571.                         [[tableView formatterAt:row :j] endBatching];
  572.  
  573.                         // Draw the grid lines if required
  574.                         if ( isGridLinesOn )
  575.                         {
  576.                             // Set the color of the lines
  577.                             NXSetColor(localGridColor);
  578.                         
  579.                             // Only put left borders on first columns and
  580.                             // if row numbers are turned off
  581.                             if ( j == colStart && !isRowNumbersEnabled )
  582.                             {
  583.                                 PSmoveto(theRect.origin.x,theRect.origin.y);
  584.                                 PSlineto(theRect.origin.x,theRect.origin.y+rowHeight);
  585.                             }
  586.                             else
  587.                                 PSmoveto(theRect.origin.x - 1,
  588.                                     theRect.origin.y+rowHeight);
  589.                             
  590.                             // Always draw bottom and right borders
  591.                             PSlineto(theRect.origin.x + theRect.size.width - 1,
  592.                                 theRect.origin.y + rowHeight);
  593.                             PSlineto(theRect.origin.x + theRect.size.width - 1,
  594.                                 theRect.origin.y);
  595.                             
  596.                             // Only put top border on first rows
  597.                             if ( actualRow == actualPageRowStart )
  598.                             {
  599.                                 if ( j == colStart )
  600.                                     PSlineto(theRect.origin.x,theRect.origin.y);
  601.                                 else PSlineto(theRect.origin.x - 1,theRect.origin.y);
  602.                             }
  603.                         }
  604.                         theRect.origin.x += theRect.size.width;
  605.                     }
  606.                     if ( isGridLinesOn || isRowNumbersEnabled ) PSstroke();
  607.                 }
  608.                 rowNumber++;
  609.                 actualRow++;
  610.                 i++;
  611.             }
  612.             theRect.origin.y--;
  613.             // advance the draw rect's origin to the next page
  614.             // The bezelForwardOffset is to compensate jumping bezelDrawOffset
  615.             // pixels after drawing the header
  616.             if ( i == maxRowsPerPage )
  617.                 theRect.origin.y += pageAdvanceOffset - bezelForwardOffset;
  618.             else
  619.                 theRect.origin.y += pageHeight -
  620.                     ((i * rowHeight) + headerTextCellSize.height +
  621.                     pageNumberHeight + bezelForwardOffset);
  622.             colStart = colEnd;
  623.  
  624.             // determine how many cols can fit on next page
  625.             for ( colsThisPage = 0,tmpDouble = 0.0,i = colStart;
  626.                     i < maxColumns; i++ )
  627.             {
  628.                 [headerTextCell setStringValue:[[columnList objectAt:i] title]];
  629.                 [headerTextCell calcCellSize:&cellSize];
  630.                 theSize = [(DBTableVector *)[columnList objectAt:i] size];
  631.                 if ( theSize < cellSize.width ) theSize = cellSize.width;
  632.                 tmpDouble += theSize;
  633.                 if ( tmpDouble > rowNumberedWidth ) break;
  634.                 else colsThisPage++;
  635.             }
  636.             if ( colsThisPage == 0 ) colsThisPage = 1;
  637.             colEnd += colsThisPage;
  638.             subPage++;
  639.         }
  640.         page++;
  641.     }
  642.  
  643.     return self;
  644. }
  645.  
  646. - read:(NXTypedStream*)stream
  647. {
  648.     int    version;
  649.  
  650.     [super read:stream];
  651.  
  652.     // Get the version
  653.     version = NXTypedStreamClassVersion(stream,"DBTableViewPrinter");
  654.     
  655.     // Read all version 0 stuff first
  656.     NXReadTypes(stream,"cccccc@",&isPrinting,&isPageNumbersEnabled,
  657.         &isRowNumbersEnabled,&isSelectedRowsOnly,&isGridLinesOn,
  658.         &isForceDrawColor,&columnList);
  659.     headerTextCell = NXReadObject(stream);
  660.     pageNumberCell = NXReadObject(stream);
  661.     rowNumberHeaderCell = NXReadObject(stream);
  662.     rowNumberCell = NXReadObject(stream);
  663.     NXReadTypes(stream,"c[51c]",&pageNumSepChar,&rowNumberHeaderTitle);
  664.     NXReadTypes(stream,"{ffff}",&pageFrame);
  665.     NXReadTypes(stream,"{ffff}",&theRect);
  666.     NXReadTypes(stream,"fffff",&rowHeight,&pageHeight,
  667.         &pageAdvanceOffset,&pageNumberHeight,&rowNumberedWidth);
  668.     NXReadTypes(stream,"{ff}{ff}{ff}",
  669.         &headerTextCellSize,&pageNumberCellSize,&rowNumberHeaderCellSize);
  670.     NXReadTypes(stream,"iiiiii",
  671.         &maxColumns,&maxRows,&maxRowsPerPage,&pagesPerRow,
  672.         &startingPageNumber,&startingRowNumber);
  673.     gridColor = NXReadColor(stream);
  674.     backgroundColor = NXReadColor(stream);
  675.     NXReadTypes(stream,"@",&tableView);
  676.  
  677.     // Read Version 1 stuff
  678.     if ( version > 0 ) rowNumbersBackColor = NXReadColor(stream);
  679.  
  680.     return self;
  681. }
  682.  
  683. - write:(NXTypedStream*)stream
  684. {
  685.     [super write:stream];
  686.  
  687.     // Write version 0 stuff first
  688.     NXWriteTypes(stream,"cccccc@",&isPrinting,&isPageNumbersEnabled,
  689.         &isRowNumbersEnabled,&isSelectedRowsOnly,&isGridLinesOn,
  690.         &isForceDrawColor,&columnList);
  691.     NXWriteObject(stream,headerTextCell);
  692.     NXWriteObject(stream,pageNumberCell);
  693.     NXWriteObject(stream,rowNumberHeaderCell);
  694.     NXWriteObject(stream,rowNumberCell);
  695.     NXWriteTypes(stream,"c[51c]",&pageNumSepChar,&rowNumberHeaderTitle);
  696.     NXWriteTypes(stream,"{ffff}",&pageFrame);
  697.     NXWriteTypes(stream,"{ffff}",&theRect);
  698.     NXWriteTypes(stream,"fffff",&rowHeight,&pageHeight,
  699.         &pageAdvanceOffset,&pageNumberHeight,&rowNumberedWidth);
  700.     NXWriteTypes(stream,"{ff}{ff}{ff}",
  701.         &headerTextCellSize,&pageNumberCellSize,&rowNumberHeaderCellSize);
  702.     NXWriteTypes(stream,"iiiiii",
  703.         &maxColumns,&maxRows,&maxRowsPerPage,&pagesPerRow,
  704.         &startingPageNumber,&startingRowNumber);
  705.     NXWriteColor(stream,gridColor);
  706.     NXWriteColor(stream,backgroundColor);
  707.     NXWriteTypes(stream,"@",&tableView);
  708.  
  709.     // write version 1 stuff
  710.     NXWriteColor(stream,rowNumbersBackColor);
  711.  
  712.     return self;
  713. }
  714.  
  715. @end
  716.  
  717. @implementation DBTableViewPrinter(Private)
  718.  
  719. // refreshLayout gets the page size and determines all size parameters like
  720. // the number of rows per page, the number of columns per page, etc.
  721.  
  722. - refreshLayout
  723. {
  724.     int                i,
  725.                         bezelDrawOffset = 0,
  726.                         maxPages;
  727.     NXRect            visibleRect;
  728.     NXSize            cellSize;
  729.     NXCoord            theSize,
  730.                         leftMargin,rightMargin,topMargin,bottomMargin;
  731.     const NXRect    *paperRect;
  732.     double            tmpDouble;
  733.     char                title[51];
  734.     id                    thePrintPanel = nil;
  735.  
  736.     // Get some formatter and size information from the tableView
  737.     columnList = [tableView columnList];
  738.     maxColumns = [columnList count];
  739.     if ( isSelectedRowsOnly ) maxRows = [tableView selectedRowCount];
  740.     else maxRows = [tableView rowCount];
  741.     if ( [tableView rowCount] > 0 ) rowHeight = [[tableView rowAt:0] size];
  742.     if ( rowHeight <= 0.0 ) rowHeight = 18;
  743.  
  744.     // Lock the table's window and make sure everything has been displayed
  745.     // if everything has not been displayed, it will not get printed, so I
  746.     // have to "force a dislay" here, even if it is off screen.
  747.     [[tableView window] disableFlushWindow];
  748.     [[[tableView docView] superview] getDocVisibleRect:&visibleRect];
  749.     for ( i = 0; i < maxColumns; i++ )
  750.         [(DBTableView *)tableView scrollColumnToVisible:i];
  751.     [tableView scrollClip:[tableView docView] to:&visibleRect.origin];
  752.     [tableView reflectScroll:[[tableView docView] superview]];
  753.     [tableView layoutChanged:self];
  754.     [[tableView window] reenableFlushWindow];
  755.  
  756.     // Get the width of a page
  757.     [[NXApp printInfo] getMarginLeft:&leftMargin right:&rightMargin
  758.         top:&topMargin bottom:&bottomMargin];
  759.     paperRect = [[NXApp printInfo] paperRect];
  760.     pageFrame.size.width =
  761.         (NXCoord)paperRect->size.width - (NXCoord)(rightMargin + leftMargin);
  762.     rowNumberedWidth = pageFrame.size.width;
  763.  
  764.     // If row numbering is on, set the row header cell to the correct width
  765.     if ( isRowNumbersEnabled )
  766.     {
  767.         // Get the size of the number of digits in maxRow
  768.         [rowNumberHeaderCell setIntValue:(maxRows + startingRowNumber) * 10];
  769.         [rowNumberHeaderCell calcCellSize:&cellSize];
  770.  
  771.         // Initialize the rowHeader sizes and values
  772.         [rowNumberHeaderCell setStringValue:rowNumberHeaderTitle];
  773.         [rowNumberHeaderCell calcCellSize:&rowNumberHeaderCellSize];
  774.  
  775.         // Keep making the Row Header wider until it's wide enough
  776.         memset(title,0,51);
  777.         strncpy(title,rowNumberHeaderTitle,50);
  778.         while ( rowNumberHeaderCellSize.width < cellSize.width &&
  779.                 strlen(title) < 45 )
  780.         {
  781.             sprintf(title," %s ",[rowNumberHeaderCell stringValue]);
  782.             [rowNumberHeaderCell setStringValue:title];
  783.             [rowNumberHeaderCell calcCellSize:&rowNumberHeaderCellSize];
  784.         }
  785.         rowNumberedWidth -= rowNumberHeaderCellSize.width;
  786.     }
  787.  
  788.     // Get total pages to print a row
  789.     for ( pagesPerRow = 1, tmpDouble = 0.0, i = 0; i < maxColumns; i++ )
  790.     {
  791.         [headerTextCell setStringValue:[[columnList objectAt:i] title]];
  792.         [headerTextCell calcCellSize:&cellSize];
  793.         theSize = [(DBTableVector *)[columnList objectAt:i] size];
  794.         if ( theSize < cellSize.width ) theSize = cellSize.width;
  795.         tmpDouble += theSize;
  796.         if ( tmpDouble > rowNumberedWidth )
  797.         {
  798.             if ( i < (maxColumns - 1) && !(theSize > rowNumberedWidth &&
  799.                 tmpDouble == theSize) ) i--;
  800.             pagesPerRow++;
  801.             tmpDouble = 0.0;
  802.         }
  803.     }
  804.  
  805.     // Set the height of the page number cell if desired
  806.     if ( isPageNumbersEnabled ) pageNumberHeight = pageNumberCellSize.height;
  807.     else pageNumberHeight = 0.0; 
  808.  
  809.     // offset to forward origin.y after drawing each header
  810.     if ( [headerTextCell isBezeled] ) bezelDrawOffset = 5;
  811.  
  812.     // Get the single page height and the whole rect needed to print this thing
  813.     pageHeight = paperRect->size.height - (topMargin + bottomMargin);
  814.     tmpDouble =
  815.         (double)((pageHeight - headerTextCellSize.height) -
  816.         (pageNumberHeight + bezelDrawOffset)) / (double)rowHeight;
  817.     maxRowsPerPage = (int)floor(tmpDouble) - 1;
  818.     tmpDouble = (double)maxRows / (double)maxRowsPerPage;
  819.     maxPages = (int)ceil(tmpDouble) * pagesPerRow;
  820.     if ( maxPages <= 0 ) maxPages = 1;
  821.     pageFrame.size.height = (NXCoord)maxPages * (NXCoord)pageHeight;
  822.  
  823.     // Size the View in which to draw
  824.     [self sizeTo:pageFrame.size.width :pageFrame.size.height];
  825.  
  826.     // Get the offset to add to advance to the next page
  827.     pageAdvanceOffset = pageHeight - ((maxRowsPerPage * rowHeight) +
  828.         headerTextCellSize.height + pageNumberHeight);
  829.  
  830.     // Get my subclass of print panel and print with the proper pagesPerRow
  831.     thePrintPanel = [TablePrintPanel new];
  832.     if ( thePrintPanel &&
  833.             [thePrintPanel respondsTo:@selector(setPagesPerRow:)] )
  834.         [thePrintPanel setPagesPerRow:pagesPerRow];
  835.  
  836.     return self;
  837. }
  838.  
  839. @end